package com.kitwee.serialport.util;


import android.os.CountDownTimer;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;

public final class SerialPortIOHelper {

    private final FileInputStream mInputStream;
    private final FileOutputStream mOutputStream;

    private Thread mReadThread;
    private Thread mWriteThread;

    private final ByteBuffer mReadBuffer;
    private final ByteBuffer mWriteBuffer;
    private static final int BUFFER_SIZE = 4 * 1024;

    private Callback mListener;

    public SerialPortIOHelper(FileDescriptor serialPort) {
        mInputStream = new FileInputStream(serialPort);
        mOutputStream = new FileOutputStream(serialPort);

        mReadThread = new ReadThread();
        mWriteThread = new WriteThread();

        mReadBuffer = ByteBuffer.allocate(BUFFER_SIZE);
        mWriteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
    }

    /**
     * 开启读写线程
     */
    public synchronized void start(Callback listener) {
        mListener = listener;
        mReadThread.start();
        mWriteThread.start();
    }

    /**
     * 读串口线程
     */
    private class ReadThread extends Thread {

        private byte[] dataFrame;

        // todo:使用标志位，如换行符（0x0d0x0a）代替计时器判定数据帧
        private final CountDownTimer frameTimer;

        private ReadThread() {
            frameTimer = new SimpleCountDownTimer(300) {
                @Override
                public void onFinish() {
                    if (dataFrame.length > 0 && mListener != null) {
                        mListener.onDataReceived(dataFrame);
                    }
                    dataFrame = new byte[]{};
                }
            };
            dataFrame = new byte[]{};
        }

        @Override
        public void run() {
            super.run();
            SLog.d("Running read thread");
            while (!isInterrupted()) {
                try {
                    int len = mInputStream.read(mReadBuffer.array());
                    if (len > 0) {
                        SLog.d("Read data len=" + len);
                        final byte[] data = new byte[len];
                        mReadBuffer.get(data, 0, len);
                        onReadBytes(data);
                        mReadBuffer.clear();
                    }

                } catch (IOException e) {
                    try {
                        SLog.e("Check fd=" + mInputStream.getFD().valid());
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                    if (mListener != null) {
                        mListener.onErrorOccurred(e);
                    }
                    SLog.e("Read thread error!");
                }
            }
        }

        private void onReadBytes(byte[] bytes) {
            frameTimer.cancel();
            dataFrame = Bytes.concatBytes(dataFrame, bytes);
            frameTimer.start();
        }
    }


    /**
     * 写串口
     *
     * @param data 数据
     */
    public void write(byte[] data) {
        synchronized (mWriteBuffer) {
            try {
                mWriteBuffer.put(data);
            } catch (BufferOverflowException e) {
                if (mListener != null) {
                    mListener.onErrorOccurred(e);
                }
                e.printStackTrace();
            }
        }
    }

    /**
     * 写串口线程
     */
    private class WriteThread extends Thread {

        private WriteThread() {
            super();
        }

        @Override
        public void run() {
            super.run();
            SLog.d("Running write thread");
            while (!isInterrupted()) {
                try {
                    byte[] outBuff = null;
                    synchronized (mWriteBuffer) {
                        int len = mWriteBuffer.position();
                        if (len > 0) {
                            outBuff = new byte[len];
                            mWriteBuffer.rewind();
                            mWriteBuffer.get(outBuff, 0, len);
                            mWriteBuffer.clear();
                        }
                    }
                    if (outBuff != null) {
                        SLog.d("Writing data len=" + outBuff.length);
                        mOutputStream.write(outBuff);
                        if (mListener != null) {
                            mListener.onDataSent(outBuff);
                        }
                    }

                } catch (IOException e) {
                    try {
                        SLog.e("Check fd=" + mOutputStream.getFD().valid());
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                    if (mListener != null) {
                        mListener.onErrorOccurred(e);
                    }
                    SLog.e("Write thread error!");
                }
            }
        }
    }

    /**
     * 读写、异常回调接口
     */
    public interface Callback {
        void onDataReceived(byte[] data);

        void onDataSent(byte[] data);

        void onErrorOccurred(Exception e);
    }


    /**
     * 停止读写线程
     */
    public synchronized void stop() {
        mReadThread.interrupt();
        mWriteThread.interrupt();
        try {
            mInputStream.close();
            mOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
